﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace YZH.Audio
{
    public delegate bool OnWaveDataArriveHandler(Object obj,byte[] data);

    /// <summary>
    /// 录音驱动
    /// 作    者：叶志辉
    /// 创建时间：
    /// 修改时间：
    ///</summary>      
    public class AudioRecorderDriver : IDisposable
    {
        #region 类型

        /// <summary>
        /// 录音缓存单元
        /// 作    者：叶志辉
        /// 创建时间：
        /// 修改时间：
        ///</summary>  
        internal class WaveInBufferItem : IDisposable
        {
            #region 字段
            private IntPtr m_WaveIn;

            private WaveHdr m_Header;
            private byte[] m_HeaderData;

            private GCHandle m_HeaderHandle;
            private GCHandle m_HeaderDataHandle;
            private GCHandle m_driverHandle;

            bool m_Recording=false;
            int _budderSize;

            private static object _lockObj = new object();
            private bool _stoping = false;
            private AutoResetEvent m_RecordEvent = new AutoResetEvent(false);
            private bool _hadData = false;
            #endregion

            #region 属性
            public bool Stoping
            {
                set
                {
                    _stoping = value;
                }
            }
            //public bool Recoding { get { return m_Recording; } }

            public int KeyID { get { return _index; } }
            #endregion

            #region 事件
            
            private bool Recording
            {
                get { return m_Recording; }
                set { 
                    m_Recording = value;
                    AudioRecorderDriver.AddMessage(string.Format("改变记录状态{0}-----{1}",m_Recording.ToString(),_index));                    
                }
            }
            int _index=0;
            #endregion

            #region 初始化接口
            public WaveInBufferItem(IntPtr waveInHandle, int size,int index=0)
            {
                _index = index;
                _budderSize = size;

                AudioRecorderDriver.AddMessage("初始化缓存单元------"+_index);
                m_WaveIn = waveInHandle;
                m_driverHandle = GCHandle.Alloc(this, GCHandleType.Normal);
                InitHandler();
                if (WaveAPI.waveInPrepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header)) != WaveAPI.MMSYSERR_NOERROR)
                {
                    FreeHandle(); 
                    if(m_driverHandle!=null&&m_driverHandle.IsAllocated)
                    {
                        m_driverHandle.Free();
                    }
                    throw new Exception("准备缓冲区错误！");
                }
            }
            private void InitHandler()
            {
                m_Header = new WaveHdr();
                m_HeaderHandle = GCHandle.Alloc(m_Header, GCHandleType.Pinned);                
                m_Header.dwUser = (IntPtr)m_driverHandle;
                m_HeaderData = new byte[_budderSize];
                m_HeaderDataHandle = GCHandle.Alloc(m_HeaderData, GCHandleType.Pinned);
                m_Header.lpData = m_HeaderDataHandle.AddrOfPinnedObject();
                m_Header.dwBufferLength = _budderSize;
            }
            ~WaveInBufferItem()
            {
                Dispose();
            }
            private void FreeHandle()
            {
                
                if (m_Header.lpData != IntPtr.Zero)
                {
                    WaveAPI.waveInUnprepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header));
                    m_HeaderHandle.Free();
                    m_Header.lpData = IntPtr.Zero;
                }
                if (m_HeaderDataHandle.IsAllocated)
                    m_HeaderDataHandle.Free();
            }
            public void Dispose()
            {
                //AudioRecorderDriver.AddMessage("准备撤销缓存单元------" + _index);
                
                //lock (WaveInBufferItem.LockObj)
                {
                    AudioRecorderDriver.AddMessage("准备关闭录音设备缓存-------" + _index);
                    FreeHandle();
                    //Thread.Sleep(200);                    
                    AudioRecorderDriver.AddMessage("录音设备缓存已关闭-------" + _index);
                }
                if (m_driverHandle != null && m_driverHandle.IsAllocated)
                {
                    m_driverHandle.Free();
                }
                GC.SuppressFinalize(this);
                //AudioRecorderDriver.AddMessage("完成撤销缓存单元------" + _index);
            }
            #endregion

            #region 处理接口
            //消息到达通知
            internal static void WaveInProc(IntPtr hdrvr, int uMsg, int dwUser, ref WaveHdr wavhdr, int dwParam2)
            {
                //lock (_lockObj)
                {
                    if (uMsg == WaveAPI.MM_WIM_DATA)
                    {
                        try
                        {
                            if (wavhdr.dwUser == IntPtr.Zero)
                            {
                                AudioRecorderDriver.AddMessage("------------------------------------缓存单元地址为零错误");
                                return;
                            }
                            
                            GCHandle h = (GCHandle)wavhdr.dwUser;
                            if (h.IsAllocated == false)
                            {
                                AudioRecorderDriver.AddMessage("------------------------------------缓存单元地址被释放或移动");
                                return;
                            }

                            WaveInBufferItem buf = (WaveInBufferItem)h.Target;
                            buf.OnCompleted();
                        }
                        catch (Exception e)
                        {
                            int i = 0;
                            i++;
                        }
                    }
                    else
                    {
                        //AudioRecorderDriver.AddMessage(string.Format("录音状态变化---------------------------------------{0}",uMsg));
                        /*
                        try
                        {
                            GCHandle h = (GCHandle)wavhdr.dwUser;
                            WaveInBufferItem buf = (WaveInBufferItem)h.Target;
                            AudioRecorderDriver.AddMessage(string.Format("录音状态变化{0}----状态值{1}",buf.KeyID,uMsg));
                        }
                        catch(Exception e)
                        {
                            AudioRecorderDriver.AddMessage("录音状态错误" +e.ToString());
                        }*/
                    }
                }
            }
            public bool Record()
            {
                //lock (_lockObj)
                {
                    AudioRecorderDriver.AddMessage("准备录音..." + _index);
                    if (_stoping) return false;
                    m_RecordEvent.Reset();
                    //WaveAPI.waveInUnprepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header));
                    /*
                    WaveAPI.waveInUnprepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header));
                    var res=WaveAPI.waveInPrepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header));
                    if (res != WaveAPI.MMSYSERR_NOERROR)
                    {
                        AudioRecorderDriver.AddMessage(string.Format("定义缓冲结构失败({0})！原因：{1}", _index, res));
                    }*/
                    var res=WaveAPI.waveInAddBuffer(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header));
                    Recording = res == WaveAPI.MMSYSERR_NOERROR;
                    if (m_Recording==false)
                    {
                        AudioRecorderDriver.AddMessage(string.Format("添加缓冲失败({0})！原因：{1};重新开缓冲-------------", _index, res));
                        WaveAPI.waveInUnprepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header));
                        FreeHandle();
                        InitHandler();
                        WaveAPI.waveInPrepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header));
                        res = WaveAPI.waveInAddBuffer(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header));
                        Recording = res == WaveAPI.MMSYSERR_NOERROR;
                    }
                    if (m_Recording == false)
                        AudioRecorderDriver.AddMessage(string.Format("添加缓冲失败({0})！原因：{1}",_index,res));
                    return m_Recording;
                }
            }

            private void OnCompleted()
            {                
                    //完成数据进队列
                    try
                    {
                        AudioRecorderDriver.AddMessage("完成录音" + _index);
                        /*
                        byte[] buf = new byte[m_Header.dwBytesRecorded];
                        Array.Copy(m_HeaderData, buf, m_Header.dwBytesRecorded);
                        Recording = false;*/
                        Thread.Sleep(50);
                        _hadData = true;
                        m_RecordEvent.Set();
                        //Record();
                    }
                    catch
                    {
                    }
                
            }

            public void Wait(int timeCount=-1)
            {
                if (m_Recording)
                {
                    if (timeCount == -1)
                    {
                        m_Recording = m_RecordEvent.WaitOne();
                        AudioRecorderDriver.AddMessage(string.Format("数据到达，等待结束：{0}", _index));
                    }
                    else
                    {
                        m_Recording = m_RecordEvent.WaitOne(timeCount);
                        AudioRecorderDriver.AddMessage(string.Format("数据未到达，等待超时：{0}", _index));
                    }
                    m_Recording = false;
                }
                else
                {
                    AudioRecorderDriver.AddMessage(string.Format("未开始记录，不需要等待：{0}", _index));
                    Thread.Sleep(10);
                }
            }

            public byte[] GetData()
            {
                if(_hadData==false) return null;
                AudioRecorderDriver.AddMessage(string.Format("准备数据...：{0}", _index));
                byte[] data = new byte[m_Header.dwBytesRecorded];
                Array.Copy(m_HeaderData, data, m_Header.dwBytesRecorded);
                _hadData = false;
                return data;
            }
            #endregion
        }

        /// <summary>
        /// 录音缓存
        /// 作    者：叶志辉
        /// 创建时间：
        /// 修改时间：
        ///</summary>  
        internal class WaveInBuffer:IDisposable
        {
            #region 字段
            private List<WaveInBufferItem> _buffer;
            private Queue<WaveInBufferItem> _preQueue = new Queue<WaveInBufferItem>();

            //private AutoResetEvent m_RecordEvent = new AutoResetEvent(false);
            #endregion

            #region 属性
            public bool Stoping
            {
                set
                {
                    foreach (var item in _buffer)
                        item.Stoping = value;
                }
            }
            
            #endregion

            #region 初始化接口
            public WaveInBuffer(IntPtr waveInHandle, int bufferCount, int bufferSize)
            {
                AudioRecorderDriver.AddMessage("初始化缓存...");
                _buffer = new List<WaveInBufferItem>(bufferCount);
                for (int i = 0; i < bufferCount; i++)
                {
                    try
                    {
                        WaveInBufferItem item = new WaveInBufferItem(waveInHandle, bufferSize,i);
                        
                        _buffer.Add(item);
                        _preQueue.Enqueue(item);
                         item.Record();
                    }
                    catch(Exception e)
                    {
                        int k = 0;
                        k++;

                    }
                }
                if (_preQueue.Count<=0) //一个缓存也没有
                {
                    throw new Exception("添加缓存失败");
                }
                AudioRecorderDriver.AddMessage("完成初始化缓存");
            }
            ~WaveInBuffer()
            {
                Dispose();
            }

            public void Dispose()
            {
                AudioRecorderDriver.AddMessage("准备撤销缓存...");
                if(_buffer!=null)
                {
                    foreach (var item in _buffer)
                        item.Dispose();
                    _buffer.Clear();
                    _buffer = null;
                }
                if (_preQueue != null)
                {
                    //lock (_preQueue)
                    {
                        _preQueue.Clear();
                    }
                }
                AudioRecorderDriver.AddMessage("完成撤销缓存");
                GC.SuppressFinalize(this);
            }
            #endregion

            #region 管理接口
            
            public void Record(WaveInBufferItem data)
            {
                bool result=data.Record();

                //Thread.Sleep(50);
                //lock (_preQueue)
                {
                    _preQueue.Enqueue(data);
                }
            }

            public void WaitForAllBuffer(int maxTime=-1)
            {
                //lock (_preQueue)
                {
                    if (_preQueue != null)
                    {
                        foreach (var item in _preQueue)
                            item.Wait(maxTime);
                    }
                }
            }
            #endregion

            #region 查询接口
            public byte[] GetData(out WaveInBufferItem driver)
            {                
                //获取最新缓存驱动
                //lock (_preQueue)
                {
                    driver = _preQueue.Dequeue();
                }
                driver.Wait();
                Thread.Sleep(50);
                return driver.GetData();
            }
            #endregion
        }

        #endregion

        #region 字段

        private IntPtr m_WaveIn;
        private WaveAPI.WaveDelegate m_BufferProc = new WaveAPI.WaveDelegate(WaveInBufferItem.WaveInProc);
        private object _lockObj = new object();

        WaveFormat _format;
        int _deviceId;

        WaveInBuffer _buffer;
        Thread _thread;

        int _bufferCount, _bufferSize;

        bool _runing = false;

        static StringBuilder _strBuilder = new StringBuilder();
        #endregion

        #region 事件
        /// <summary>
        /// 音频数据到达
        /// </summary>
        public OnWaveDataArriveHandler OnWaveDataArrive;
        private bool RaiseOnWaveDataArrive(Object obj, byte[] data)
        {
            try
            {
                var tmp = OnWaveDataArrive;
                if (tmp != null)
                    return tmp(obj, data);
                return false;
            }
            catch { return false; }
        }
        #endregion

        #region 属性
        /// <summary>
        /// 设备数量
        /// </summary>
        public static int DeviceCount
        {
            get { return WaveAPI.waveInGetNumDevs(); }
        }

        public WaveFormat WaveFormat
        {
            get
            {
                return _format;
            }
        }
        #endregion

        #region 初始化接口
        public AudioRecorderDriver()
        {
            _format = new WaveFormat(16000, 16, 1);
            _deviceId = WaveAPI.WAVE_MAPPER;
            _bufferCount = 2;
            _bufferSize = 6400;
            AddMessage("初始化驱动...");

            
        }
        public AudioRecorderDriver(WaveFormat format,int device=-1, int bufferCount=3, int bufferSize=6400)
		{
            _deviceId = device;
            _format = format;
            _bufferCount = bufferCount;
            _bufferSize = bufferSize;
		}

        ~AudioRecorderDriver()
		{
			Dispose();
		}
		public void Dispose()
		{
            AddMessage("关闭驱动资源...");
            Stop();
            
			GC.SuppressFinalize(this);
            AddMessage("完成关闭驱动资源");
		}
        #endregion

        #region 执行接口
        private void ThreadProc()
        {
            try
            {
                AddMessage("启动线程...");
                while (_runing)
                {
                    AddMessage("等待数据...");
                    WaveInBufferItem driver;
                    var data = _buffer.GetData(out driver);

                    if (data != null)
                        RaiseOnWaveDataArrive(this, data);
                    AddMessage("数据处理完成");
                    AddMessage("准备添加侦听缓存...");
                    if (_runing == false)
                        return;
                    _buffer.Record(driver);
                }
            }
            catch (Exception e)
            {
                int i = 0;
                AddMessage(e.ToString());
                i++;
            }
            AddMessage("关闭线程");
        }

        public bool Start()
        {
            lock (_lockObj)
            {
                if (_thread != null && _runing) return true;
                _runing = true;
                AddMessage("打开录音设备");
                int result = WaveAPI.waveInOpen(out m_WaveIn, _deviceId, _format, m_BufferProc, 0, (int)WaveInOpenFlags.CALLBACK_FUNCTION);
                if (result != WaveAPI.MMSYSERR_NOERROR)
                {
                    _runing = false;
                    throw new Exception("打开录音设备错误");
                }
                AddMessage("准备生成录音缓存...");
                //生成缓存
                _buffer = new WaveInBuffer(m_WaveIn, _bufferCount, _bufferSize);
                //启动线程
                try
                {
                    _thread = new Thread(new ThreadStart(ThreadProc));
                }
                catch (Exception e)
                {
                    _runing = false;
                    _buffer.Dispose();
                    _thread = null;
                    throw e;
                }
                AddMessage("打开录音设备");
                if (WaveAPI.waveInStart(m_WaveIn) != WaveAPI.MMSYSERR_NOERROR)
                {
                    _runing = false;
                    _buffer.Dispose();
                    _thread = null;
                    throw new Exception("启动录音设备出错");
                }

                _thread.Start();
                return true;
            }
        }

        public void Stop()
        {
            lock (_lockObj)
            {
                if (_thread != null)
                {
                    _runing = false;
                    try
                    {
                        if (m_WaveIn != IntPtr.Zero)
                            WaveAPI.waveInReset(m_WaveIn);

                        AddMessage("准备关闭缓存...");
                        _thread.Abort();
                        if (_thread.ThreadState == System.Threading.ThreadState.Running)
                        {
                            _thread.Join();
                        }

                        if (_buffer != null)
                        {
                            _buffer.WaitForAllBuffer(2000);
                        }

                        if (_buffer != null)
                        {
                            _buffer.Stoping = true;
                            _buffer.Dispose();
                        }
                        AddMessage("成功关闭缓存");
                        //_thread.Abort();                    
                        AddMessage("准备停止录音设备...");
                        if (m_WaveIn != IntPtr.Zero)
                            WaveAPI.waveInStop(m_WaveIn);
                        Thread.Sleep(50);
                        if (m_WaveIn != IntPtr.Zero)
                            WaveAPI.waveInClose(m_WaveIn);
                        AddMessage("停止录音设备");
                    }
                    catch { }
                    finally
                    {
                        _thread = null;
                        m_WaveIn = IntPtr.Zero;
                    }
                    _thread = null;
                }
            }
        }

        public static void SaveFile(string fileName,WaveFormat format,byte[] data)
        {
            try{
                FileStream stream=new FileStream(fileName,FileMode.OpenOrCreate,FileAccess.Write,FileShare.Read);
                stream.SetLength(0);
                BinaryWriter writer=new BinaryWriter(stream);
                int  m_WaveHeaderSize = 38;
                int  m_WaveFormatSize = 18;
                writer.Write(System.Text.Encoding.ASCII.GetBytes("RIFF".ToCharArray()));
                int totalsize=4+m_WaveHeaderSize;
                writer.Write(totalsize);
                writer.Write(System.Text.Encoding.ASCII.GetBytes("WAVE".ToCharArray()));
                writer.Write(System.Text.Encoding.ASCII.GetBytes("fmt ".ToCharArray()));
                writer.Write(m_WaveFormatSize);

                writer.Write(format.wFormatTag);
	            writer.Write(format.nChannels);
	            writer.Write(format.nSamplesPerSec);
	            writer.Write(format.nAvgBytesPerSec);
	            writer.Write(format.nBlockAlign);
	            writer.Write(format.wBitsPerSample);
	            writer.Write(format.cbSize);
                writer.Write(System.Text.Encoding.ASCII.GetBytes("data".ToCharArray()));
                writer.Write(data.Length);
                writer.Write(data);
                writer.Flush();
                stream.Close();
            }
            catch
            {}
        }

        [Conditional("TEST")]
        internal static void AddMessage(string msg)
        {            
            lock(_strBuilder)
            {
                _strBuilder.AppendFormat(string.Format("{0}---{1}\r\n",DateTime.Now, msg));
            }
        }
        public static string GetMsg()
        {
            lock(_strBuilder)
            {
                string str= _strBuilder.ToString();
                _strBuilder.Clear();
                return str;
            }
        }
        #endregion

        #region 辅助函数

        #endregion
    }
}
